<script>

function teelog(x) {
    console.log(x)
    return x
}

(function () {

    var socket_url = function (suffix) {
        var loc = window.location, new_uri;
        if (loc.protocol === "https:") {
            new_uri = "wss:";
        } else {
            new_uri = "ws:";
        }
        new_uri += "//" + loc.host;
        new_uri += "/socket/" + suffix;
        return new_uri;
    }

    var commands = {
        patch: function (cmd) {
            Escher.applyPatch(cmd.id, cmd.data)
        },
        import: function (cmd) {
            var ln = document.createElement("link")
            ln.rel = "import"
            ln.href = cmd.data
            document.head.appendChild(ln)
        },
        log: function (cmd) {
            console.log(cmd)
        }
    }

    var w = window.innerWidth,
        h = window.innerHeight

        Escher = {}
        Escher.init = function (suffix) {
            if (typeof(window.Blink) === "undefined") {
                var url = socket_url(suffix) + "?w=" + w + "&h=" + h,
                    ws = new WebSocket(url)

                ws.onmessage = function (msg) {
                    var cmd = JSON.parse(msg.data),
                        f = commands[cmd.command]
                    if (!f) {
                        console.log("Invalid command", cmd.command, "invoked")
                    } else {
                        f.call(Escher, cmd)
                    }
                }

                ws.onerror = function (e) {
                    console.log("Error in web socket connection", e)
                }
                Escher.send = function (data) {
                    ws.send(JSON.stringify(data))
                }

            } else {
                Escher.send = function (data) {
                    Blink.msg("escher", data)
                }
            }
            window.addEventListener("resize", function (ev) {
                // TODO: Throttle
                Escher.send(
                    { command: "window-size",
                      data: [ window.innerWidth, window.innerHeight ]
                    }
                )
            })


            var closeHandler = function () {
                Escher.send({command: "window-kill", data: null})
                // prevent firing twice
                window.removeEventListener("beforeunload", closeHandler)
            }

            window.addEventListener("beforeunload", closeHandler)

              document.addEventListener("signal-transport", function (ev) {
                  Escher.send({
                      command: "signal-update",
                      data: {
                          signalId: ev.detail.signalId,
                          value: ev.detail.value
                      }
                  })
            })
        }
    // Store for patches of nodes as yet uncreated
    Escher.patches = {}
    function stopSpinner(node) {
      try {
      var spnode = node.element && node.element.parentNode && node.element.parentNode.node
      if (spnode && spnode.stopSpinner) { spnode.stopSpinner() }
      } catch (err) {
        console.log("Error stopping spinner", err);
      }
    }
    Escher.applyPatch = function (id, patch) {
        if (typeof(Escher.patches[id]) === "undefined") {
            Escher.patches[id] = []
        }
        if (typeof(Escher.subtrees.ready[id]) !== "undefined") {
            var node = Escher.subtrees.ready[id];

            stopSpinner(node);
            node.applyPatch(patch)
        } else if (typeof(Escher.subtrees.destroyed[id]) !== "undefined") {
            console.log("Patch received for a previously destroyed subtree. This is why we can't have good things.")
        } else {
            Escher.patches[id].push(patch)
        }
    }

    Escher.addSubtree = function (id, node) {
        delete Escher.subtrees.destroyed[id]
        Escher.subtrees.ready[id] = node
        if (typeof(Escher.patches[id]) !== "undefined") {
            for (var i=0, l=Escher.patches[id].length; i < l; i++) {
                stopSpinner(node);
                node.applyPatch(Escher.patches[id][i])
            }
        }
    }

    Escher.removeSubtree = function (id) {
        delete Escher.subtrees.ready[id]
        Escher.subtrees.destroyed[id] = true
    }

    Escher.subtrees = {ready: {}, destroyed: {}}
    window.Escher = Escher


  function Sneaky (node) {
    var lightNode = Polymer.dom(node)
    this.lightNode = lightNode
    this.node = lightNode.node
  }

  Sneaky.prototype = Polymer.dom()
  Sneaky.prototype.setAttributeNS = function (ns, name, value) {
    this.node.setAttributeNS(ns, name, value);
    this.lightNode._distributeParent();
  }

  Object.defineProperties(Sneaky.prototype, {

    childNodes: {
      get: function() {
        var nodes = this.lightNode.childNodes
        return nodes.map(function (node) { return new Sneaky(node) })
      },
      configurable: true
    },

    children: {
      get: function() {
        var nodes = this.lightNode.children
        return nodes.map(function (node) { return new Sneaky(node) })
      },
      configurable: true
    },

    parentNode: {
      get: function() {
        return this.lightNode ? new Sneaky(this.lightNode.parentNode) : null
      },
      configurable: true
    }
  });

  function getNode(x) {
    return x.node ? x.node : x
  }

  // forward mutations to light DOM
  Sneaky.prototype.appendChild = function (node) {
    return new Sneaky(this.lightNode.appendChild(getNode(node)))
  }

  Sneaky.prototype.removeChild = function (node) {
    return new Sneaky(this.lightNode.removeChild(getNode(node)))
  }

  Sneaky.prototype.insertBefore = function (node, refNode) {
    return new Sneaky(this.lightNode.insertBefore(getNode(node), refNode && getNode(refNode) || null))
  }

  window.Sneaky = Sneaky;
  window.sneakyDoc = {
    document: {
      createElement: function (x, y) {
        return new Sneaky(document.createElement(x, y))
      },
      createElementNS: function (x, y) {
        return new Sneaky(document.createElementNS(x, y))
      },
      createTextNode: function (txt) {
        return document.createTextNode(txt)
      },
    },
    extractNode: getNode
  }

  // Polymer mixins (polymer calls them behaviors)
  var EscherMixins = {}
  window.EscherMixins = EscherMixins

  EscherMixins.LifeCycle = {
    _domInitCalled: false,
    attached: function () {

      // Use domInit instead of attached.
      // attached is called twice in some places
      // when using virtual-dom

      if (!this._domInitCalled) {
        this.domInit && this.domInit()
        this._domInitCalled = true
      }
    }
  }

})();
</script>

<dom-module
  id="signal-container">

<style>
#content {
  display: inherit;
}
</style>

<template>
  <div id="content">
    <content></content>
  </div>
</template>

<script>

  Polymer({

    is: "signal-container",
    behaviors: [EscherMixins.LifeCycle],

    properties: {
      signalId: {
        type: String,
        observer: "_idChanged"
      },
      vnode: Object
    },

    stopSpinner: function () {
      var el = this.querySelector("#spinner")
      if (el) {
        el.style.display = "none";
      }
    },

    _initCleanSubtree: function (id) {
        // XXX: Initial value of tile signals is Escher.empty which is <div></div>
        this.$.content.innerHTML = "<div></div>";
        this.vnode = new Patchwork.Node(id, {t: "div"}, new Sneaky(this.$.content), sneakyDoc);
        Escher.addSubtree(id, this.vnode)
        this._prevId = id
    },

    domInit: function () {
      var id = this.signalId
      this._initCleanSubtree(id)
    },

    _idChanged: function (id, oldId) {
        if (typeof(oldId) !== "undefined") {
            // This means we are in a SignalWrap inside a SignalWrap
            // (...which is probably inside another SignalWrap).
            // console.log("ID changed", this.signalId, this._prevId, this.vnode)

            Escher.removeSubtree(oldId)
            this._initCleanSubtree(id)
        }
    }
  })
</script>

</dom-module>
